/*
**  Copyright (c) 2014 Michael Liu(michael.liu.point@gmail.com).
**
**  Project: Gateway Unified Management Platform
**  File:      dpmgr.c
**  Author:  Michael
**  Date:    03/20/2014
**
**  Purpose:
**    data path management implement.
*/

/* Include files. */

#include "dpmgr.h"
#include "svcmgr.h"
#include "osdep.h"
#include "pfdep.h"

/* Macro constant definitions. */

#define DATA_PATH_CHECK_OPT_MODE_WORK		"check_opt_mode"

#define NANO_SECOND_PER_MICRO_SECOND		(1000)
#define MICRO_SECOND_PER_SECOND				(1000)

#define DP_TASK_WAIT_SECOND					1

#define IPT_DNS_REDIRECT_UDP_RULE	"-A PREROUTING -p udp -m udp --dport 53 -j REDIRECT --to-ports 53"
#define IPT_DNS_REDIRECT_TCP_RULE	"-A PREROUTING -p tcp -m tcp --dport 53 -j REDIRECT --to-ports 53"

/* Type definitions. */

typedef struct dp_priv {
	tos_dep_mutex mutex;
	tos_dep_cond cond;
	struct list_head cmd_list;
	struct list_head work_list;
} tdp_priv;

/* Local function declarations. */

static void *dp_work_task(void *arg);
static e_ret start_dp_work_task(tdata_paths * dp);
static e_ret dp_work_task_proc_cmd(tdp_priv * priv, tdp_cmd * cmd);
static void rt_mode_sw_check_status(tdp_work * me, void *arg);

static e_ret dp_add_dnsmasq_svc();
static e_ret dnsmasq_svc_start(tsm_svc * svc);
static e_ret dnsmasq_svc_reload(tsm_svc * svc);

/* External function declarations. */

extern e_ret dp_cmn_proc_req(tdata_paths * dp, tcm_dp_req_body * req, tcm_dp_rsp_body ** rsp, int *size);
extern e_ret dp_if_proc_req(tdata_paths * dp, tcm_dp_req_body * req, tcm_dp_rsp_body ** rsp, int *size);
extern e_ret dp_rule_proc_req(tdata_paths * dp, tcm_dp_req_body * req, tcm_dp_rsp_body ** rsp, int *size);
extern e_ret http_redirect_stop(void);

/* Macro API definitions. */

/* Global variable declarations. */

tdata_paths *data_paths;

e_ret data_path_init(tdata_paths * dp)
{
	e_ret ret;

	sysmgr_trace_enter();

	ASSERT(dp != NULL);

	ret = start_dp_work_task(dp);
	if (ret != eRET_SUCCESS) {
		sysmgr_err("start_dp_work_task() failed, ret: %d.\n", ret);
		sysmgr_trace_exit();
		return (ret);
	}

	dp_add_dnsmasq_svc();

	ret = dp_ifs_init(dp);
	if (ret != eRET_SUCCESS) {
		sysmgr_err("dp_ifs_init() failed, ret: %d.\n", ret);
		sysmgr_trace_exit();
		return (ret);
	}

	data_paths = dp;

	sysmgr_trace_exit();
	return (eRET_SUCCESS);

}

e_ret data_path_start(void)
{

	tdp_work check_opt_mode;
	extern tsa_dev rt_mode_sw;

	/* Add Device Operation Mode check work. */

	check_opt_mode.arg = &rt_mode_sw;
	str_safe_cpy(check_opt_mode.name, DATA_PATH_CHECK_OPT_MODE_WORK, DP_WORK_NAME_STR_MAX - 1);
	check_opt_mode.work = rt_mode_sw_check_status;

	dp_task_add_work(data_paths, &check_opt_mode);

	/* Enable IP forward */
	return (eRET_SUCCESS);
}

e_ret dp_add_dnsmasq_svc()
{
	tsm_svc *svc;
	tdnsmasq_svc_priv *priv;

	sysmgr_trace_enter();

	priv = malloc(sizeof(tdnsmasq_svc_priv));
	if (priv == NULL) {
		sysmgr_err("malloc(%d) failed, err: %d,[%s]\n", (sizeof(tdnsmasq_svc_priv)), errno, strerror(errno));
		sysmgr_trace_exit();
		return (eRET_NO_RESOURCE);
	}
	priv->dp = NULL;
	priv->dp_if = NULL;
	priv->dp_rule = NULL;

	svc = malloc(sizeof(tsm_svc));
	if (svc == NULL) {
		sysmgr_err("malloc(%d) failed, err: %d,[%s]\n", (sizeof(tsm_svc)), errno, strerror(errno));
		free(priv);
		sysmgr_trace_exit();
		return (eRET_NO_RESOURCE);
	}
	memset(svc, 0, sizeof(tsm_svc));
	os_dep_mutex_init(&svc->mutex);
	strcpy(svc->name, SYSMGR_SVC_DNSMASQ);
	svc->enable = eBOOL_TRUE;
	svc->start = dnsmasq_svc_start;
	svc->reload = dnsmasq_svc_reload;
	svc->priv = priv;

	dnsmasq_svc_start(svc);

	svcmgr_add_svc(svc);

	sysmgr_trace_exit();
	return (eRET_SUCCESS);

}

e_ret dnsmasq_svc_start(tsm_svc * svc)
{
	int rc;
	tdnsmasq_svc_priv *priv;

	sysmgr_trace_enter();

	os_dep_mutex_lock(&svc->mutex);
	svc->task = 0;

	os_dep_killall("dnsmasq", "-SIGTERM");
	usleep(1000);
	/*
	 **  NOW: SVC -> task == 0,
	 **  So we can direct send Child signal to make sure process has been Reclaim.
	 **  It's a double check.
	 */
	os_dep_killall("sysmgr", "-SIGCHLD");
	usleep(1000);

	priv = svc->priv;

	dpif_update_dhcpd_cfg((tdp_if *) priv->dp_if);

	dpif_update_dns_addr_cfg(priv->dp_rule);

	/* Add DNS package capture rule. */
	if (priv->dp != NULL) {
		dp_rules_add_ipt_rule(priv->dp, eIPT_RULE_TABLE_NAT, IPT_DNS_REDIRECT_UDP_RULE, NULL);
		dp_rules_add_ipt_rule(priv->dp, eIPT_RULE_TABLE_NAT, IPT_DNS_REDIRECT_TCP_RULE, NULL);
		dp_rules_apply_ipt_rule(priv->dp);
	}

	rc = os_dep_do_system_raw(eBOOL_FALSE, &svc->task, "dnsmasq", "-7", "/etc_ro/dnsmasq.d", "-d", NULL);
	sysmgr_dbg("os_dep_do_system_raw(dnsmasq), rc: %d\n", rc);

	os_dep_mutex_unlock(&svc->mutex);

	sysmgr_trace_exit();
	return (eRET_SUCCESS);
}

e_ret dnsmasq_svc_reload(tsm_svc * svc)
{

#if 0
	int rc = 0;

	sysmgr_trace_enter();
	if (svc->task != 0) {
		rc = os_dep_killall("dnsmasq", "-SIGHUP");
		sysmgr_dbg("os_dep_killall(dnsmasq, SIGHUP), rc: %d\n", rc);
	}

	if (rc != 0) {
		/* service not exist. */
		rc = os_dep_killall("dnsmasq", "-SIGTERM");
		usleep(1000);
		dnsmasq_svc_start(svc);
	}

	sysmgr_trace_exit();

#else

	sysmgr_trace_enter();

	/*
	 ** dnsmasq do not reload config file properly
	 ** kill it and system service manager will start it.
	 */

	os_dep_killall("dnsmasq", "-SIGTERM");
	usleep(1000);
	/*
	 **  NOW: SVC -> task == 0,
	 **  So we can direct send Child signal to make sure process has been Reclaim.
	 **  It's a double check.
	 */
	os_dep_killall("sysmgr", "-SIGCHLD");
	usleep(1000);

	sysmgr_trace_exit();

#endif

	return (eRET_SUCCESS);
}

e_ret data_path_proc_req(tcm_req_body * req, tcm_rsp_body ** rsp)
{
	e_ret ret = eRET_SUCCESS;
	int size = 0;
	tcm_dp_req_body *dp_req;
	tcm_dp_rsp_body *dp_rsp = NULL;
	tcm_rsp_body *new_rsp = NULL;

	sysmgr_trace_enter();

	dp_req = (tcm_dp_req_body *) req->mod_req_body;
	switch (dp_req->submod) {
	case eDP_SUB_MOD_DP_IF:
		ret = dp_if_proc_req(data_paths, dp_req, &dp_rsp, &size);
		break;

	case eDP_SUB_MOD_DP_RULE:
		ret = dp_rule_proc_req(data_paths, dp_req, &dp_rsp, &size);
		break;

	case eDP_SUB_MOD_DP_COMMON:
		ret = dp_cmn_proc_req(data_paths, dp_req, &dp_rsp, &size);
		break;

	default:
		ret = eRET_INVALID_ARG;
		break;
	}

	if (dp_rsp != NULL) {
		dp_rsp->submod = dp_req->submod;
		new_rsp = malloc(sizeof(*new_rsp) + size);
		if (new_rsp == NULL) {
			sysmgr_err("malloc(%d) failed, err: %d,[%s]\n",
				   (sizeof(*new_rsp) + size), errno, strerror(errno));
			ret = eRET_NO_RESOURCE;
			goto out;
		} else {
			new_rsp->ret = ret;
			new_rsp->rsp_body_len = size;
			memcpy(new_rsp->mod_rsp_body, dp_rsp, size);
			*rsp = new_rsp;
		}
		free(dp_rsp);
	}

out:
	*rsp = new_rsp;

	sysmgr_trace_exit();
	return (ret);
}

e_ret start_dp_work_task(tdata_paths * dp)
{

	int rc;
	tdp_priv *priv;
	pthread_t dp_work_thread;

	sysmgr_trace_enter();

	priv = malloc(sizeof(*priv));
	if (priv == NULL) {
		sysmgr_err("malloc(%d) failed, err: %d,[%s]\n", (sizeof(tdnsmasq_svc_priv)), errno, strerror(errno));
		sysmgr_trace_exit();
		return (eRET_NO_RESOURCE);
	}
	dp->priv = priv;

	INIT_LIST_HEAD(&priv->cmd_list);
	INIT_LIST_HEAD(&priv->work_list);

	os_dep_mutex_init(&priv->mutex);
	os_dep_cond_init(&priv->cond);

	rc = pthread_create(&dp_work_thread, NULL, dp_work_task, dp);
	if (rc != 0) {
		sysmgr_err("pthread_create( dp work task ) failed, err: %d.\n", rc);
		sysmgr_trace_exit();
		return (eRET_FAILURE);
	}

	sysmgr_trace_exit();
	return (eRET_SUCCESS);

}

void *dp_work_task(void *arg)
{
	int rc;
	unsigned long counter;
	tdp_cmd *cmd;
	tdp_work *work;
	struct list_head *cmd_node;
	struct list_head *each, *next;
	tdata_paths *dp = (tdata_paths *) arg;
	tdp_priv *priv = dp->priv;
	struct timespec timeout;
	struct timeval tv;

	sysmgr_trace_enter();
	sysmgr_info("dp_work_task() start.\n");

	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);

	counter = 0;
	while (1) {
		os_dep_mutex_lock(&priv->mutex);

		gettimeofday(&tv, NULL);
		timeout.tv_sec = tv.tv_sec + DP_TASK_WAIT_SECOND;
		timeout.tv_nsec = tv.tv_usec * NANO_SECOND_PER_MICRO_SECOND;

		rc = os_dep_cond_timedwait(&priv->cond, &priv->mutex, &timeout);
		sysmgr_dbg("os_dep_cond_timedwait() rc:%d\n", rc);

		cmd_node = NULL;
		if (!list_empty(&priv->cmd_list)) {
			cmd_node = priv->cmd_list.next;
			list_del(cmd_node);
		}
		os_dep_mutex_unlock(&priv->mutex);

		if (cmd_node != NULL) {
			cmd = list_entry(cmd_node, tdp_cmd, list);
			dp_work_task_proc_cmd(priv, cmd);
			free(cmd);
		}

		/* Do Works. */
		list_for_each_safe(each, next, &priv->work_list) {
			work = list_entry(each, tdp_work, list);
			if (!work->done) {
				work->work(work, work->arg);
			}

			/* Check again for self deleted */
			if (work->done) {
				sysmgr_dbg("Remove Work[%s]\n", work->name);
				list_del(&work->list);
				free(work);
			}
		}

	}

	sysmgr_trace_exit();

	return (NULL);

}

e_ret dp_work_task_proc_cmd(tdp_priv * priv, tdp_cmd * cmd)
{

	e_bool found;
	tdp_work *work;
	struct list_head *each;

	sysmgr_trace_enter();

	switch (cmd->cmd_opt) {
	case eDP_CMD_OPT_ADD_WORK:
		found = eBOOL_FALSE;
		list_for_each(each, &priv->work_list) {
			work = list_entry(each, tdp_work, list);
			if (strcmp(work->name, cmd->work.name) == 0) {
				/* remove old one */
				work->done = eBOOL_TRUE;
				break;
			}
		}

		work = malloc(sizeof(*work));
		if (cmd == NULL) {
			sysmgr_err("malloc(%d) failed, err: %d,[%s]\n", sizeof(*work), errno, strerror(errno));
			return (eRET_NO_RESOURCE);
		}
		*work = cmd->work;
		work->done = eBOOL_FALSE;
		sysmgr_dbg("Add Work[%s]\n", work->name);
		list_add_tail(&work->list, &priv->work_list);
		break;

	case eDP_CMD_OPT_DEL_WORK:
		list_for_each(each, &priv->work_list) {
			work = list_entry(each, tdp_work, list);
			if (strcmp(work->name, cmd->work.name) == 0) {
				work->done = eBOOL_TRUE;
				break;
			}
		}
		break;

	default:
		sysmgr_dbg("Unknown CMD: %d\n", cmd->cmd_opt);
	}

	sysmgr_trace_exit();
	return (eRET_SUCCESS);

}

e_ret dp_task_opt_work(tdata_paths * dp, tdp_work * work, e_bool add)
{
	tdp_cmd *cmd;
	tdp_priv *priv = dp->priv;

	sysmgr_trace_enter();

	if (work->name[0] == '\0') {
		sysmgr_trace_exit();
		return (eRET_INVALID_ARG);
	}

	cmd = malloc(sizeof(*cmd));
	if (cmd == NULL) {
		sysmgr_err("malloc(%d) failed, err: %d,[%s]\n", sizeof(*cmd), errno, strerror(errno));
		sysmgr_trace_exit();
		return (eRET_NO_RESOURCE);
	}

	if (add) {
		cmd->cmd_opt = eDP_CMD_OPT_ADD_WORK;
	} else {
		cmd->cmd_opt = eDP_CMD_OPT_DEL_WORK;
	}
	cmd->work = *work;

	INIT_LIST_HEAD(&cmd->list);

	os_dep_mutex_lock(&priv->mutex);
	list_add_tail(&cmd->list, &priv->cmd_list);
	os_dep_cond_signal(&priv->cond);
	os_dep_mutex_unlock(&priv->mutex);

	sysmgr_trace_exit();
	return (eRET_SUCCESS);

}

void rt_mode_sw_check_status(tdp_work * me, void *arg)
{
	tsa_dev *mode_sw = (tsa_dev *) arg;

	if ((mode_sw != NULL) && (mode_sw->opt != NULL)) {
		mode_sw->opt(mode_sw, eMODE_SW_OPT_CHECK, NULL);
	}
}

inline int is_progress_status_group(int i)
{
	if ((i == 3) || ((i >= 10) && (i <= 26))) {
		return 1;
	}
	return 0;
}

inline int is_idle_notused_status_group(int i)
{
	if ((i == 0) || (i == 1)) {
		return 1;
	}
	return 0;
}

e_ret dp_wlan_wps_proc(unsigned char button_event)
{

	tdp_if *dp_if;
	struct list_head *each;
	tdata_paths *dp = data_paths;

	if (dp->mode == eDP_MODE_BRIDGE_REPEATER) {
		list_for_each(each, &dp->all_ifs) {
			dp_if = list_entry(each, tdp_if, all_list);
			if (mask_test_bit(dp_if->attr, eDP_IF_ATTR_LINK_WPS) &&
			    ((dp_if->type == eDP_IF_TYPE_WLAN24G_CLI) || (dp_if->type == eDP_IF_TYPE_WLAN5G_CLI))) {
				dp_wlan_cli_start_wps(dp_if, eWLAN_WPS_DPID_PBC, 0);
			}
		}
	} else {
		list_for_each(each, &dp->all_ifs) {
			dp_if = list_entry(each, tdp_if, all_list);
			if (mask_test_bit(dp_if->attr, eDP_IF_ATTR_LINK_WPS) &&
			    ((dp_if->type == eDP_IF_TYPE_WLAN24G_AP) || (dp_if->type == eDP_IF_TYPE_WLAN5G_AP))) {
				if (button_event == eBUTTON_INPT_SHORT_PRESS) {
					/* Short press, 2.4G only */
					if (dp_if->type != eDP_IF_TYPE_WLAN24G_AP) {
						continue;
					}
				} else {
					/* Long press, 5G only */
					if (dp_if->type != eDP_IF_TYPE_WLAN5G_AP) {
						continue;
					}
				}
				dp_wlan_ap_start_wps(dp_if, eWLAN_WPS_DPID_PBC, 0);
			}
		}
	}

	return (eRET_SUCCESS);
}

void dp_if_wlan_wps_work(tdp_work * me, void *arg)
{

	e_ret ret;
	int status;
	twlan_wsc_profile prof;
	tdp_if *dp_if = (tdp_if *) arg;
	tdata_paths *dp = dp_if->data_paths;

	if (dp_if->wps.counter++ > 120) {
		sysmgr_err("dp_if_wlan_wps_work[%s] time up.\n", me->name);
		dp_if_set_led_status(dp_if, eLED_STATUS_WPS_FAIL);
		goto finish;
	}

	ret = pf_dep_wlan_get_wsc_status(dp_if, &status);
	if (ret != eRET_SUCCESS) {
		sysmgr_err("pf_dep_wlan_get_wsc_status() failed, ret: %d.\n", ret);
		return;
	}

	dp_if->wps.status = status;

	sysmgr_dbg("dp if[%s] wsc status %d[%s].\n", dp_if->name, status, pd_def_get_wsc_status_str(status));

	if (status == -1) {
		return;
	}

	switch (status) {
	case 0:		/* Not used */
	case 1:		/* Idle */
		dp_if_set_led_status(dp_if, eLED_STATUS_SIGNAL_NONE);
		dp_if_set_led_status(dp_if, eLED_STATUS_INIT);
		goto finish;
		break;

	case 2:		/* WSC Fail */
		if (dp_if->wps.cfg_method == eWLAN_WPS_DPID_PIN) {
			/* Incorrect PIN code ? */
			dp_if_set_led_status(dp_if, eLED_STATUS_WPS_FAIL);
			goto finish;
		} else {
			dp_if_set_led_status(dp_if, eLED_STATUS_WPS_FAIL);
			goto finish;
		}
		break;

	case 34:		/* Configured */
		memset(&prof, 0, sizeof(prof));
		ret = pf_dep_wlan_get_wsc_profile(dp_if, &prof);
		if (ret != eRET_SUCCESS) {
			sysmgr_err("pf_dep_wlan_get_wsc_profile() failed, ret: %d.\n", ret);
			return;
		}
		sysmgr_info("config	  :[%d]\n", prof.wsc_configured);
		sysmgr_info("SSID	  :[%s]\n", prof.wsc_ssid);
		sysmgr_info("auth mode:[%d]\n", prof.wsc_auth_mode);
		sysmgr_info("encr type:[%d]\n", prof.wsc_encryp_type);
		sysmgr_info("defaultid:[%d]\n", prof.wsc_default_key_idx);
		sysmgr_info("key 	  :[%s]\n", prof.wsc_wpapsk);

		if ((prof.wsc_configured & 0x0F) == 2) {
			dp_if_set_led_status(dp_if, eLED_STATUS_WPS_SUCCESS);
			if ((dp_if->type == eDP_IF_TYPE_WLAN24G_CLI) || (dp_if->type == eDP_IF_TYPE_WLAN5G_CLI)) {
				tmask mask;
				unsigned char channel;
				unsigned char ext_channel;
				tdp_work *work;
				tdp_if *other_cli_if;
				struct list_head *each;
				tdp_priv *priv = dp->priv;
				twlan_cli_if_linkcfg linkcfg;
				unsigned char other_cli_if_type;

				memset(&linkcfg, 0, sizeof(linkcfg));
				memset(&mask, 0, sizeof(mask));

				pf_dep_cfg_init();

				str_safe_cpy(linkcfg.ssid, prof.wsc_ssid, WLAN_SSID_SIZE - 1);
				mask_set_bit(mask.mask[0], eWLAN_CLI_IF_LINKCFG_MASK0_SSID);

				channel = ((prof.wsc_configured >> 8) & 0xFF);
				linkcfg.channel = channel;
				ext_channel = ((prof.wsc_configured >> 6) & 0x03);
				if (ext_channel == 0x01) {
					linkcfg.ext_channel = eWLAN_EXT_CHANNEL_ABOVE;
				} else if (ext_channel == 0x03) {
					linkcfg.ext_channel = eWLAN_EXT_CHANNEL_BELOW;
				} else {
					linkcfg.ext_channel = eWLAN_EXT_CHANNEL_NONE;
				}
				mask_set_bit(mask.mask[0], eWLAN_CLI_IF_LINKCFG_MASK0_CHANNEL);

				if (prof.wsc_auth_mode == 0x0001) {
					linkcfg.auth_mode = eWLAN_AUTH_MODE_OPEN;
				} else if (prof.wsc_auth_mode == 0x0002) {
					linkcfg.auth_mode = eWLAN_AUTH_MODE_WPAPSK;
				} else if (prof.wsc_auth_mode == 0x0004) {
					linkcfg.auth_mode = eWLAN_AUTH_MODE_SHARED;
				} else if (prof.wsc_auth_mode == 0x0008) {
					linkcfg.auth_mode = eWLAN_AUTH_MODE_WPA;
				} else if (prof.wsc_auth_mode == 0x0010) {
					linkcfg.auth_mode = eWLAN_AUTH_MODE_WPA2;
				} else if (prof.wsc_auth_mode == 0x0020) {
					linkcfg.auth_mode = eWLAN_AUTH_MODE_WPA2PSK;
				} else if (prof.wsc_auth_mode == 0x0022) {
					linkcfg.auth_mode = eWLAN_AUTH_MODE_WPAPSKWPA2PSK;
				} else {
					linkcfg.auth_mode = eWLAN_AUTH_MODE_OPEN;
				}
				mask_set_bit(mask.mask[0], eWLAN_CLI_IF_LINKCFG_MASK0_AUTH_MODE);

				if (prof.wsc_encryp_type == 0x0001) {
					linkcfg.enc_method = eWLAN_ENC_METHOD_NONE;
				} else if (prof.wsc_encryp_type == 0x0002) {
					linkcfg.enc_method = eWLAN_ENC_METHOD_WEP;
					linkcfg.sec_key.wep_key.default_id = prof.wsc_default_key_idx;
					strcpy(linkcfg.sec_key.wep_key.key[linkcfg.sec_key.wep_key.default_id].value,
					       prof.wsc_wpapsk);
				} else if (prof.wsc_encryp_type == 0x0004) {
					linkcfg.enc_method = eWLAN_ENC_METHOD_TKIP;
					strcpy(linkcfg.sec_key.wpa_psk, prof.wsc_wpapsk);
				} else if (prof.wsc_encryp_type == 0x0008) {
					linkcfg.enc_method = eWLAN_ENC_METHOD_AES;
					strcpy(linkcfg.sec_key.wpa_psk, prof.wsc_wpapsk);
				} else if (prof.wsc_encryp_type == 0x000C) {
					linkcfg.enc_method = eWLAN_ENC_METHOD_TKIP_AES;
					strcpy(linkcfg.sec_key.wpa_psk, prof.wsc_wpapsk);
				} else {
					linkcfg.enc_method = eWLAN_ENC_METHOD_NONE;
				}
				mask_set_bit(mask.mask[0], eWLAN_CLI_IF_LINKCFG_MASK0_ENC_METHOD);
				mask_set_bit(mask.mask[0], eWLAN_CLI_IF_LINKCFG_MASK0_SEC_KEY);

				ret = dp_if_do_cmd(dp_if, eDP_IF_METHOD_SET_LINKCFG, &linkcfg, &mask, NULL);
				if (ret != eRET_SUCCESS) {
					sysmgr_err("dp_if_do_cmd(%s, eDP_IF_METHOD_SET_LINKCFG) "
						   "failed[0x%08lX][0x%08lX], ret: %d\n", dp_if->name, mask.mask[0],
						   mask.mask[1], ret);
				}

				/* Remove other ap client wps process */
				if (dp_if->type == eDP_IF_TYPE_WLAN24G_CLI) {
					other_cli_if_type = eDP_IF_TYPE_WLAN5G_CLI;
				} else {
					other_cli_if_type = eDP_IF_TYPE_WLAN24G_CLI;
				}
				list_for_each(each, &priv->work_list) {
					work = list_entry(each, tdp_work, list);
					if (strstr(work->name, DP_IF_WPS_WORK_SUBFIX) != NULL) {
						other_cli_if = (tdp_if *) work->arg;
						if (other_cli_if->type == other_cli_if_type) {
							work->done = eBOOL_TRUE;
							dp_if_set_led_status(other_cli_if, eLED_STATUS_SIGNAL_NONE);
							pf_dep_wlan_set_wsc_conf_mode(dp_if, 0);
#ifndef APCLI_BOND_SUPPORT
							dp_if_do_cmd(other_cli_if, eDP_IF_METHOD_UNLINK, NULL);
							pf_dep_wlancli_set_linkenable(other_cli_if, eBOOL_FALSE,
										      eBOOL_FALSE);
#endif /* APCLI_BOND_SUPPORT */
						}
					}
				}
				/* Stop HTTP Redirect */
				http_redirect_stop();
				pf_dep_set_qis(eBOOL_TRUE);
				pf_dep_cfg_save();
			}
			goto finish;
		}
		break;

#if 0
	case 36:
		if (dp->mode == eDP_MODE_BRIDGE_REPEATER) {
			tdp_work *work;
			tdp_if *other_cli_if;
			struct list_head *each;
			tdp_priv *priv = dp->priv;
			unsigned char other_cli_if_type;

			/* Remove other ap client wps process */
			if (dp_if->type == eDP_IF_TYPE_WLAN24G_CLI) {
				other_cli_if_type = eDP_IF_TYPE_WLAN5G_CLI;
			} else {
				other_cli_if_type = eDP_IF_TYPE_WLAN24G_CLI;
			}
			list_for_each(each, &priv->work_list) {
				work = list_entry(each, tdp_work, list);
				if (strstr(work->name, DP_IF_WPS_WORK_SUBFIX) != NULL) {
					other_cli_if = (tdp_if *) work->arg;
					if (other_cli_if->type == other_cli_if_type) {
						work->done = eBOOL_TRUE;
						dp_if_set_led_status(other_cli_if, eLED_STATUS_SIGNAL_NONE);
						dp_if_do_cmd(other_cli_if, eDP_IF_METHOD_UNLINK, NULL);
						pf_dep_wlancli_set_linkenable(other_cli_if, eBOOL_FALSE, eBOOL_FALSE);
					}
				}
			}
		}
		break;
#endif

	case 0x109:		/* Overlap detection */
		dp_if_set_led_status(dp_if, eLED_STATUS_WPS_OVERLAP);
		goto finish;
		break;

	default:
		break;
	}
	return;

finish:
	dp_if->wps.counter = 0;
	dp_if->wps.cfg_method = eWLAN_WPS_DPID_NOT_SPEC;

	if ((dp_if->type == eDP_IF_TYPE_WLAN24G_AP) || (dp_if->type == eDP_IF_TYPE_WLAN5G_AP)) {
		twlan_ap_acl acl;
		twlan_ap_if_linkcfg *linkcfg = (twlan_ap_if_linkcfg *) dp_if->linkcfg;

		dp_if_set_led_status(dp_if, eLED_STATUS_INIT);

		if (linkcfg != NULL) {
			/* Apply ACL after WPS done */
			acl = linkcfg->acl;
			pf_dep_wlanap_set_acl(dp_if, &acl);

			/* Update WPS config */
			if ((linkcfg->wps_enable) && (linkcfg->wps_configed)) {
				if (linkcfg->wps_pin != 0) {
					pf_dep_wlan_set_wsc_vendor_pin_code(dp_if, linkcfg->wps_pin);
				}
				pf_dep_wlan_set_wsc_conf_mode(dp_if, 0);
				pf_dep_wlan_set_wsc_conf_mode(dp_if, 7);
				pf_dep_wlan_set_wsc_conf_status(dp_if, 2, eBOOL_FALSE);
			}
		}
	} else {
		dp_if_do_cmd(dp_if, eDP_IF_METHOD_LINK, NULL);
	}

	me->done = eBOOL_TRUE;
	return;
}
